home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#44 (May 89)
/
Cursor Stuff
/
AnimCurs.c
< prev
next >
Wrap
Text File
|
1989-02-17
|
18KB
|
399 lines
/* Animated Cursor subroutines.
Copyright © 1989 by Michael S. Morton, all rights reserved.
Written February ’89 for MacTutor. Feel free to use these or
modify them so long as the original author and source are noted.
Routines included are:
• acStartHG -- set the cursor to be an hourglass
• acStartW1, acStartW2 -- set the cursor to be a 1- or 2-handed watch
• acDelay -- set the delay, in ticks, between animation steps
• acNext -- idle-time routine
To set up any of these cursors, call the acStart___ routine, passing the
delay you’d like between frames. Then call acNext during your idle
routine. To change the delay, call acDelay. To get a different kind
of cursor, stop calling acNext and use SetCursor to get it.
*/
/* State information for three cursor types: */
typedef struct /* info on the HOURGLASS: */
{ long coin; /* used in random number generator */
int grainRow; /* row number (0…15) of falling grain */
int grainBit; /* bit mask for falling grain */
Boolean grainMoving; /* flag: is the grain still moving? */
} hourInfo; /* define this type */
typedef struct /* info on ONE-HANDED WATCH: */
{ int wFrame; /* which frame of animation are we on? */
} watch1Info; /* define this type */
typedef struct /* info on TWO-HANDED WATCH: */
{ int mFrame; /* frame of minute hand */
int hFrame; /* frame of hour hand */
} watch2Info; /* define this type */
/* Generalized cursor type: */
typedef enum
{ hgType, w1Type, w2Type } cursType; /* selects one of above structs */
typedef struct /* tracks any kind of cursor */
{ Cursor curs; /* current cursor */
int delay; /* delay between frames (ticks) */
long nextFrame; /* time of next frame (ticks) */
cursType type; /* type of cursor */
union /* depending on type of cursor: */
{ hourInfo hg; /* • info on hourglass */
watch1Info w1; /* • info on one-handed watch */
watch2Info w2; /* • info on two-handed watch */
} u; /* give union an easy name */
} cursInfo, *ciPtr;
/* The cursor information is used globally all through this file. */
cursInfo theInfo; /* sole instance of this struct */
/* 16x16 data and masks for cursors. Some of these are OR’d together to
make the two-handed watch. Others are just backgrounds which are
drawn on, such as for the hourglass. */
int hourData [16] = /* hourglass, about to start falling */
{ 0xFFFE, 0x7FFC, 0x7FFC, 0x7FFC, 0x5FF4, 0x4FE4, 0x47C4, 0x4384,
0x4284, 0x46C4, 0x4C64, 0x5834, 0x701C, 0x600C, 0x4004, 0xFFFE };
int hourMask [16] =
{ 0xFFFE, 0x7FFC, 0x7FFC, 0x7FFC, 0x5FF4, 0x4FE4, 0x47C4, 0x4384,
0x4384, 0x47C4, 0x4FE4, 0x5FF4, 0x7FFC, 0x7FFC, 0x7FFC, 0xFFFE };
int watchMask [16] =
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x7F00, 0xFF80, 0xFF80, 0xFFC0,
0xFFC0, 0xFF80, 0x7F00, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 };
int watchMinutes [12][16] = /* twelve minute-hand positions */
{
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4900, 0x8880, 0x8880, 0x88C0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4300, 0x8480, 0x8480, 0x88C0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8180, 0x8680, 0x88C0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8FC0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
0x86C0, 0x8180, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
0x84C0, 0x8480, 0x4300, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
0x88C0, 0x8880, 0x4900, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
0x90C0, 0x9080, 0x6100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
0xB0C0, 0xC080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0xF8C0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0xC080, 0xB080, 0x88C0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x6100, 0x9080, 0x9080, 0x88C0,
0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 }
};
int watchHours [8][16] = /* eight hour-hand positions */
{
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8880, 0x8880, 0x8880,
0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8480, 0x8880,
0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8E80,
0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8880,
0x8480, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8880,
0x8880, 0x8880, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8880,
0x9080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0xB880,
0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
{ 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x9080, 0x8880,
0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 }
};
/* Function prototypes for stuff we define externally: */
#include "AnimCurs.h"
/* Function prototypes for our static stuff: */
void acStartW (cursType type, int delay); /* start either kind of watch */
void copy16 (int *src, Bits16 *dst); /* copy a 16x16 bit image */
void or16 (int *src, Bits16 *dst); /* OR a 16x16 bit image */
void hideGrain (void); /* hide current grain in hourglass */
void showGrain (void); /* show current grain in hourglass */
Boolean coinFlip (void); /* produce a pseudo-random boolean */
void stealGrain (void); /* remove a grain from top section */
void dropGrain (void); /* get a grain moving */
void moveGrain (int dh, int dv); /* move a grain in transit */
void nextHour (void); /* compute next cursor: hourglass */
void nextWatch (void); /* watch */
void nextWatch2 (void); /* 2-handed watch */
void doNextFrame (void); /* compute next frame and show it */
/* acStartHG () -- Initialize the hourglass cursor, with the specified delay
between animation frames. This is an external routine. */
void acStartHG (delay)
int delay; /* INPUT: pause between frames */
{ theInfo.delay = delay; /* record delay in structure */
theInfo.nextFrame = TickCount () + delay; /* figure when to animate next */
theInfo.type = hgType; /* remember the type: hourglass */
theInfo.u.hg.grainRow = -1; /* make a grain drop in dropGrain () */
/* Initialize cursor data, mask and hotspot (altho dropGrain does data, too) */
copy16 (hourData, & (theInfo.curs.data));
copy16 (hourMask, & (theInfo.curs.mask));
theInfo.curs.hotSpot.v = 8; /* stick hotspot… */
theInfo.curs.hotSpot.h = 8; /* …in middle */
dropGrain (); /* start first grain dropping */
} /* end of acStartHG () */
/* acStartW1 () -- Just like acStartHG, but with a one-handed watch.
This is an external routine.
*/
void acStartW1 (delay)
int delay; /* INPUT: pause between frames */
{ acStartW (w1Type, delay);
} /* end of acStartW1 () */
/* acStartW2 () -- Just like acStartHG, but with a two-handed watch.
This is an external routine.
*/
void acStartW2 (delay)
int delay; /* INPUT: pause between frames */
{ acStartW (w2Type, delay);
} /* end of acStartW2 () */
/* acStartW () -- Start a one- or two-handed watch. */
static void acStartW (type, delay)
cursType type; /* INPUT: w1Type or w2Type */
int delay; /* INPUT: pause between frames */
{ theInfo.delay = delay; /* record delay in structure */
theInfo.nextFrame = TickCount () + delay; /* figure when to animate next */
theInfo.type = type; /* remember the type */
if (type == w1Type) /* init different types: */
theInfo.u.w1.wFrame = 0; /* one-handed: set frame to zero */
else
{ theInfo.u.w2.mFrame = 0; /* two-handed: set minute frame… */
theInfo.u.w2.hFrame = 0; } /* …and hour frame to zero */
/* Initialize the cursor data, mask and hotspot: */
copy16 (watchMinutes [0], & (theInfo.curs.data));
copy16 (watchMask, & (theInfo.curs.mask));
theInfo.curs.hotSpot.v = 8; /* stick hotspot… */
theInfo.curs.hotSpot.h = 8; /* …in middle */
} /* end of acStartW () */
/* copy16 () -- Copy an array of 16 ints into a Bits16 structure (the data
or mask of a cursor. */
void copy16 (src, bits)
register int *src; /* INPUT: source data */
Bits16 *bits; /* OUTPUT: where to stuff it */
{ register int *dst = (int *) bits; /* coerce to a handier data type */
int i; /* FORTRAN-ish loop counter */
for (i = 0; i <= 15; i++) *dst++ = *src++; /* copy 16 words */
} /* end of copy16 () */
/* or16 () -- Just like copy16(), but we OR, not COPY. */
void or16 (src, bits)
register int *src; /* INPUT: source data */
Bits16 *bits; /* OUTPUT: where to stuff it */
{ register int *dst = (int *) bits; /* coerce to a handier data type */
int i; /* FORTRAN-ish loop counter */
for (i = 0; i <= 15; i++) *dst++ |= *src++; /* OR 16 words */
} /* end of or16 () */
/* hideGrain () -- Hide the pixel where the current grain is. */
static void hideGrain ()
{ theInfo.curs.data [theInfo.u.hg.grainRow] &=
(~ theInfo.u.hg.grainBit);
}
/* showGrain () -- Show the pixel where the current grain is. */
static void showGrain ()
{ theInfo.curs.data [theInfo.u.hg.grainRow] |=
theInfo.u.hg.grainBit;
}
/* coinFlip () -- Return a pseudo-random boolean value. We use a shift-
register system detailed in Knuth, among other places. */
static Boolean coinFlip ()
{ register Boolean result;
if (! theInfo.u.hg.coin) /* zero? */
theInfo.u.hg.coin = 1; /* avoid demon state */
result = (theInfo.u.hg.coin < 0); /* note if high bit is set */
theInfo.u.hg.coin <<= 1; /* shift old value over 1 bit… */
if (result) /* …and if top bit was ‘1’… */
theInfo.u.hg.coin ^= 0xc5; /* …then futz with low bits */
return (result);
} /* end of coinFlip () */
/* stealGrain -- Grab a grain of sand from the top chamber of the hourglass.
We want to percolate a white pixel up from the starting bit position. */
static void stealGrain ()
{ register int v = 6; /* start at row 6, tip of top half */
register int hBit = 0x0100; /* start at the middle */
register Boolean done = false; /* haven’t finished percolating yet */
register int aboveWord; /* row of sand grains above us */
register Boolean leftOK, rightOK; /* are there bits to steal above? */
while (! done)
{ --v; /* assume we can move up a row */
aboveWord = theInfo.curs.data [v]; /* get that row from the cursor image */
/* Decide which direction to steal from -- if there's a grain right above
us, we’ll take that. Otherwise, we have to choose between the ones
diagonally up from us. */
if ((aboveWord & hBit) == 0) /* have a bit above us? */
{ leftOK = (0 != (aboveWord & (hBit << 1))); /* have someone above on left? */
rightOK = (0 != (aboveWord & (hBit >> 1))); /* someone above on right? */
/* Branch, depending on which way(s) we can go: */
if (leftOK && rightOK) /* both available? */
{ if (coinFlip ()) hBit <<= 1; /* yes: flip a coin… */
else hBit >>= 1; /* …to decide */
}
else if (leftOK) hBit <<= 1; /* just left: take it */
else if (rightOK) hBit >>= 1; /* just right: take it */
else { ++v; done = true; } /* neither: back down */
} /* end of no bit above us */
if (v <= 1) done = true; /* if we hit top row, we have to stop */
} /* end of while-not-done */
theInfo.curs.data [v] &= (~ hBit); /* snuff this bit */
} /* end of stealGrain () */
/* dropGrain () -- Get next grain of sand to fall. The “moving” flag is false.
We get one grain of sand from the top chamber. If the bottom chamber is
filled, we reinit the cursor to a full top chamber. Either way, we flag
that a grain is moving, and draw it.
*/
static void dropGrain ()
{ stealGrain (); /* pull a grain from the top chamber */
/* If the pile in the bottom chamber has filled it, the most recent
grain dropped didn’t get very far. */
if (theInfo.u.hg.grainRow < 9) /* are we piled too high? */
copy16 (hourData, & (theInfo.curs.data)); /* yes: redraw all */
theInfo.u.hg.grainRow = 8; /* set row number of new grain */
theInfo.u.hg.grainBit = 0x0100; /* set bit in row */
theInfo.u.hg.grainMoving = true; /* we’re cooking with gas now */
showGrain (); /* show the grain */
} /* end of dropGrain () */
/* moveGrain () -- Move the current grain by a given delta. Note that a
positive “dh” is a right shift. */
static void moveGrain (dh, dv)
int dh, dv; /* INPUT: amount to move by */
{ hideGrain (); /* hide where it is now */
theInfo.u.hg.grainRow += dv; /* update vertical position */
if (dh > 0) /* update horizontal position */
theInfo.u.hg.grainBit >>= dh;
else theInfo.u.hg.grainBit <<= -dh;
showGrain (); /* show where it is now */
} /* end of moveGrain () */
/* nextHour () -- Advance the hourglass by one frame. */
static void nextHour ()
{ register int v, hBit; /* coordinate and bit pos of grain */
register int nextWord; /* word of grains below us */
register Boolean leftOK, rightOK; /* flags for OK directions to fall */
if (! theInfo.u.hg.grainMoving) /* grain hit bottom last time? */
dropGrain (); /* yes: get a new one */
v = theInfo.u.hg.grainRow; /* grab position and bit just… */
hBit = theInfo.u.hg.grainBit; /* …for easier typing & efficiency */
nextWord = theInfo.curs.data [v+1]; /* get word below this one */
if ((nextWord & hBit) == 0) /* slot below us free? */
{ moveGrain (0, 1); /* yes: just move down one */
return; /* and that’s all we need */
}
leftOK = (0 == (nextWord & (hBit << 1))); /* have nobody below on left? */
rightOK = (0 == (nextWord & (hBit >> 1))); /* nobody below on right? */
/* Branch, depending on which way(s) we can go: */
if (leftOK && rightOK) /* can fall left or right? */
{ if (coinFlip ()) /* yes: randomly choose: */
moveGrain (1, 1); /* • fall to the right */
else moveGrain (-1, 1); /* • fall to the left */
}
else if (leftOK) moveGrain (-1, 1); /* left only: do it */
else if (rightOK) moveGrain (1, 1); /* right only: do it */
else theInfo.u.hg.grainMoving = false; /* can’t move: stop; reset next time */
} /* end of nextHour () */
/* nextWatch -- Compute the next frame of one-handed watch animation. */
static void nextWatch ()
{ ++ theInfo.u.w1.wFrame; /* bump frame ahead by 1 */
if ((theInfo.u.w1.wFrame) >= 12) /* outside of range 0…11? */
theInfo.u.w1.wFrame = 0; /* yes: wrap around */
copy16 (watchMinutes [theInfo.u.w1.wFrame],
& (theInfo.curs.data));
} /* end of nextWatch () */
/* nextWatch2 -- Compute the next frame of two-handed watch animation. */
static void nextWatch2 ()
{ ++ theInfo.u.w2.mFrame; /* bump MINUTE frame ahead by 1 */
if ((theInfo.u.w2.mFrame) >= 12) /* outside of range 0…11? */
theInfo.u.w2.mFrame = 0; /* yes: wrap around */
copy16 (watchMinutes [theInfo.u.w2.mFrame],
& (theInfo.curs.data));
if (theInfo.u.w2.mFrame == 0) /* MINUTE frame just hit 12 o’clock? */
{ ++ theInfo.u.w2.hFrame; /* yes: bump HOUR frame ahead by 1 */
if ((theInfo.u.w2.hFrame) >= 8) /* outside of range 0…7? */
theInfo.u.w2.hFrame = 0; /* yes: wrap around */
}
/* Add hour-hand image into minute-hand. */
or16 (watchHours [theInfo.u.w2.hFrame],
& (theInfo.curs.data));
} /* end of nextWatch () */
/* doNextFrame -- Compute next frame, dispatching on type of cursor. */
static void doNextFrame ()
{ switch (theInfo.type)
{ case hgType: nextHour (); break;
case w1Type: nextWatch (); break;
case w2Type: nextWatch2 (); break;
default: break;
} /* end of switch on cursor type */
} /* end of doNextFrame () */
/* acNext -- Idle-time animation. This is an external routine.
*/
void acNext ()
{ if (TickCount () < theInfo.nextFrame) /* time to animate yet? */
return; /* nope: return */
theInfo.nextFrame = TickCount () + theInfo.delay; /* reset wake-up call */
doNextFrame (); /* draw next frame internally */
SetCursor (& (theInfo.curs)); /* and display it */
} /* end of acNext () */
/* acDelay -- Set delay, in ticks, between frames. This is an external routine.
*/
void acDelay (delay)
int delay; /* INPUT: new delay */
{ theInfo.delay = delay;
theInfo.nextFrame = TickCount () + delay; /* figure when to animate next */
} /* end of acDelay () */